using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
using util;

namespace TS
{
    //represents the TS server the client is conected to
    class Server
    {
        private LinkedList<Chanel> chanels = new LinkedList<Chanel>();
        private TreeView tree = new TreeView();
        private Process getC, getS, switchCan, tcC;
        private bool conected = true;
        private char[] dill = { ' ' };
        private string loc;
        
        
        //loc is the location of the TS Client DIR so for standard install it would be C:\Program Files\Teamspeak2_RC2\
        public Server(String loc)
        {
            //set up process to access ts
            this.loc = loc;
            getC = new Process();
            getS = new Process();
            tcC = new Process();
            switchCan = new Process();
            setUpProssess(getC, "GET_CHANNELS");//well get all channels
            setUpProssess(getS, "GET_SPEAKERS");//well get the speakers
            setUpProssess(switchCan, "SWITCH_CHANNEL");//will allow user to switch channels
            setUpProssess(tcC, "");//used for any other comand to TS that I need

            //set up the Tree's Icons
            tree.ImageList = new ImageList();
            tree.ImageList.Images.Add(TS.Properties.Resources.channel);
            tree.ImageList.Images.Add(TS.Properties.Resources.notSpeaking);
            tree.ImageList.Images.Add(TS.Properties.Resources.talking);
            tree.Scrollable = true;

            //test if conected
            conected = isConected();

            //set up Events
            tree.DoubleClick += new EventHandler(tree_DoubleClick);
        }
        /***************************All events for Server***********************************/
        private void tree_DoubleClick(object o, EventArgs e)
        {
            TreeNode n = tree.SelectedNode;
            Chanel p = null;
            String pass = "";
            foreach (Chanel c in chanels)
                if (c.node == n)
                    p = c;
            if (p != null)
            {
                if (p.isPass())
                {
                    getPassDia dia = new getPassDia("This Channel is password protected, please enter the password:");
                    dia.setPassedChar('*');
                    dia.ShowDialog();
                    if (dia.exit_opp == getPassDia.OK)
                    {
                        pass = dia.UserIN;
                    }
                }
                switchCan.StartInfo.Arguments = "SWITCH_CHANNEL " + p.getID() + " " + pass;
                switchCan.Start();
            }
        }
        /************************util methods***********************/
        //gets the location of the TreeView which represents the server
        public System.Drawing.Point getTreeLoc()
        {
            return new System.Drawing.Point(tree.Location.X, tree.Location.Y);
        }
        //get the size of the TreeView which repressents the server
        public System.Drawing.Size getTreeSize()
        { return new System.Drawing.Size(tree.Size.Width, tree.Size.Height); }
        //Tests to see if the TS client is connected to a server
        public bool isConected()
        {
            getC.Start();
            getC.WaitForExit();
            StreamReader f = getC.StandardOutput;
            string a = f.ReadLine();
            f.Close();
            if (a.Equals("ERROR -1 NOT CONNECTED") || a.Equals("ERROR -1000 TEAMSPEAK CLIENT NOT FOUND"))
                return false;
            else
                return true;
        }
        //adds the TreeView to the PXPForm
        public void addToForm(PXP.Core.PXPForm f)
        {
            tree.Size = new System.Drawing.Size(250, 400);
            tree.Location = new System.Drawing.Point(10,10);
            update();
            f.pnlMainContainer.Size = new System.Drawing.Size(270, 420);
            f.pnlMainContainer.Controls.Add(tree);
        }
        //sets up a process that will call the API
        private void setUpProssess(Process p, string args)
        {
            p.StartInfo.FileName = "\"" + loc + "\\client_sdk\\tsControl.exe" + "\"";
            p.StartInfo.Arguments = args;
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.RedirectStandardOutput = true;
            p.StartInfo.CreateNoWindow = true;
        }

        /******************************update methods**************************/
        //updates the tree/server
        public void update()
        {
            conected = isConected();
            if (conected)
            {
                update_chanels();
                update_speaker();
            }
            else
            {
                tree.Nodes.Clear();
                clear();
            }
        }
        //clears the tree/server
        private void clear()
        {
            foreach (Chanel c in chanels)
                c.clear();
            chanels.Clear();
        }
        //updates the names, players and chanels that are on the server
        private void update_chanels()
        {
            //see if any new chanels have been created
            //Console.WriteLine(getC.StartInfo.FileName);
            //Console.WriteLine(getC.StartInfo.Arguments);
            try
            {
                LinkedList<Chanel> inuse = new LinkedList<Chanel>();
                getC.Start();
                getC.WaitForExit();
                StreamReader f = getC.StandardOutput;
                string a = f.ReadLine();
                if(a.Equals("ERROR -1000 TEAMSPEAK CLIENT NOT FOUND"))//ts is not runing
                {
                    tree.Nodes.Clear();
                }
                char[] dill = { ' ' };
                if (a.Equals("OK"))
                {
                    try
                    {
                        int numChanels = 0, id, subchanel;
                        string name, flag;
                        bool exists = false;
                        a = f.ReadLine();
                        string[] split = a.Split(dill);
                        if (split.Length >= 2)
                            numChanels = int.Parse(split[1]);
                        for (int i = 0; i < numChanels; i++)
                        {
                            int j = 0;
                            a = f.ReadLine();
                            split = a.Split(dill);
                            id = int.Parse(split[1]);
                            subchanel = int.Parse(split[4]);
                            name = split[7];
                            for (j = 8; j < split.Length  && !split[j].Equals("Playercount:"); j++)//we are doing this incase of spaces is the user name
                            {
                                name = name + " " + split[j];
                            }
                            flag = split[j+4];
                            exists = false;
                            Chanel temp = new Chanel(id, subchanel, name, flag, loc, tree);
                            foreach (Chanel c in chanels)//see if chanell exists
                                if (c.getID() == temp.getID())//the Id is in use
                                  {
                                        exists = true;
                                        if (!c.flag.Equals(temp.flag))//flages have been changed update
                                        {
                                            c.flag = temp.flag;
                                            c.node.Text = c.Name + c.parseFlag();
                                        }
                                        if (!c.Name.Equals(temp.Name))//just incase a ID gets reused
                                        {
                                            c.Name = temp.Name;
                                            c.node.Text = name + c.parseFlag();
                                            tree.Nodes.Find(c.getID().ToString(),true)[0].Text = name + c.parseFlag();
                                        }
                                        
                                        temp = c;
                                  }
                              inuse.AddLast(temp);
                              if (!exists)
                                {
                                    temp.node.Name = temp.getID().ToString();
                                    temp.node.Text = temp.Name + temp.parseFlag();
                                    if (temp.parent == -1)
                                    {
                                        tree.Nodes.Add(temp.node);
                                    }
                                    else
                                    {
                                        foreach (Chanel c in inuse)
                                            if (c.ID == temp.parent)
                                                c.node.Nodes.Add(temp.node);
                                    }
                                }
                                
                        }
                        foreach (Chanel c in chanels)
                        {
                            if(!inuse.Contains(c))//nolonger in tree
                                tree.Nodes.Remove(c.node);
                        }
                        chanels = inuse;
                  
                    }//end try
                    catch (Exception z)
                    { /*Console.WriteLine(z);*/ }
                }//end if
                
                f.Close();
            }//end update()
            catch (Exception e)
            { /*Console.WriteLine(e.ToString());*/ }
            foreach (Chanel c in chanels)
            {
                c.update();
            }
        }
        //updates who is speaking
        private void update_speaker()
        {
            getS.Start();
            getS.WaitForExit();
            StreamReader f = getS.StandardOutput;
            String a = f.ReadLine();
            if (a.Equals("OK"))
            {
                a = f.ReadLine();
                int nums = 0;
                string[] split = a.Split(dill);
                if (split.Length >= 2)
                    nums = int.Parse(split[1]);
                foreach (Chanel c in chanels)
                    c.clearSpeaker();
                for (int i = 0; i < nums; i++)
                {
                    a = f.ReadLine();
                    foreach (Chanel c in chanels)
                        c.setSpeaker(int.Parse(a));
                }
            }

        }

        /*****************stuf for debuging*******************/
        public override string ToString()
        {
            string a = "";
            foreach (Chanel c in chanels)
                a += c.ToString() + "\n";
            return a;
        }
    }



    //Class represents on chanel in the TS server
    class Chanel
    {
        //every chanle has an ID, as well as a parent(for sub chanes otherwise its -1)
        public string flag;///the chanal flags
        public int ID;//chanal ID as given by the API
        public int parent;//is ID of parent chanel
        public string Name;
        private char[] dill = { ' ', '(', ')' };//and utill char array to split strings
        private Process getCinfo = new Process();//a process wich is used to get infromation about a channel, like what players are currently in the channel
        private Process sendText = new Process();
        private TreeView tree;// a refrence to the tree
        public LinkedList<Player> players = new LinkedList<Player>();//proboaly could have used an array, but I can do that in V2.0
        private string loc;//the location of the TS API
        public TreeNode node;//the TreeNode which represents the obj
        private MenuItem textC;

        public Chanel(int ID, int parentID, string name, string flag, string loc,TreeView tree)
        {
            this.flag = flag;
            this.tree = tree;
            this.loc = loc;
            this.ID = ID;
            this.parent = parentID;
            this.Name = name;
            setUpProssess(getCinfo, "GET_CHANNEL_INFO " + ID);
            setUpProssess(sendText, "");
            node = new TreeNode(name);

            //set up node
            textC = new MenuItem("Send Text Message to Channel", new EventHandler(textC_Click));
            node.ContextMenu = new ContextMenu();
            node.ContextMenu.MenuItems.Add(textC);
        }
        //sends a text mesage to this channel
        private void textC_Click(object o, EventArgs e)
        {
            util.getPassDia dia = new getPassDia("Enter message to be sent to Channel: "+Name);
            dia.ShowDialog();
            if (dia.exit_opp == getPassDia.OK)
            {
                sendText.StartInfo.Arguments = "SEND_MESSAGE_CHANNEL " + ID + " " +dia.UserIN;
                sendText.Start();
            }
        }
        //parses the flag to somthing more readable
        public string parseFlag()
        {
            switch (flag)
            {
                case "0x01":
                    return " (R)";
                case "0x09":
                    return " (RP)";
                case "0x05":
                    return " (RM)";
                case "0x21":
                    return " (RD)";
                case "0x11":
                    return " (RS)";
                case "0x0D":
                    return " (RPM)";
                case "0x19":
                    return " (RPS)";
                case "0x15":
                    return " (RMS)";
                case "0x29":
                    return " (RPD)";
                case "0X31":
                    return " (RSD)";
                case "0x1D":
                    return " (RMPS)";
                case "0x35":
                    return " (RMSD)";
                case "0x02":
                    return " (U)";
                case "0x0A":
                    return " (UP)";
                case "0x06":
                    return " (UM)";
                case "0x12":
                    return " (US)";
                case "0x0E":
                    return " (UPM)";
                case "0x1A":
                    return " (UPS)";
                case "0x16":
                    return " (UMS)";
                case "0x1E":
                    return " (UPMS)";
            }
            return "";
        }
        //clears the chanal 
        public void clear()
        {
            players.Clear();
        }

        //returns true if the channal is password protected
        public bool isPass()
        {
            if (flag.Equals("0x09") || flag.Equals("0x0D") || flag.Equals("0x29") || flag.Equals("0x2D") ||
                flag.Equals("0x0A") || flag.Equals("0x0E") || flag.Equals("0x2A") || flag.Equals("0x2E") ||
                flag.Equals("0x19") || flag.Equals("0x1D") || flag.Equals("ox1A") || flag.Equals("0x1E")) 
                return true;
            return false;
        }

        //sets up the proccess for me to call the TS API
        private void setUpProssess(Process p, string args)
        {
            p.StartInfo.FileName = "\"" + loc + "\\client_sdk\\tsControl.exe" + "\"";
            p.StartInfo.Arguments = args;
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.RedirectStandardOutput = true;
            p.StartInfo.CreateNoWindow = true;
        }

        //updates this channel
        public void update()
        {

            getCinfo.Start();
            getCinfo.WaitForExit();
            StreamReader f = getCinfo.StandardOutput;
            string a = f.ReadLine();
            if (a.Equals("ERROR -1000 TEAMSPEAK CLIENT NOT FOUND"))//ts is not runing
            {
            }
            if (a.Equals("OK"))
            {
                LinkedList<Player> inuse = new LinkedList<Player>();
                //update tree with new chanel info
                //update players in chanell
                int nump = 0;
                f.ReadLine();
                f.ReadLine();
                a = f.ReadLine();
                string[] split = a.Split(dill);
                if (split.Length >= 3)
                    nump = int.Parse(split[3]);
                for (int i = 0; i < nump; i++)
                {
                    bool exists = false;
                    a = f.ReadLine();
                    split = a.Split(dill);
                    String name = split[7];
                    for (int j = 8; j < split.Length && !split[j].Equals("ChannelPrivs:"); j++)
                    {
                        name = name + " " + split[j];
                    }
                    Player temp = new Player(int.Parse(split[1]), name);
                    foreach (Player p in players)//see if chanell exists
                        if (p.getID() == temp.getID())//the Id is in use
                        {
                            exists = true;
                            temp = p;
                            if (!p.getNickName().Equals(temp.getNickName()))//just incase a ID gets reused
                            {
                                p.NickName = temp.getNickName();
                                node.Nodes.Find(p.getID().ToString(),false)[0].Text = split[7];
                            }
                            temp = p; 
                        }
                    inuse.AddLast(temp);
                    if (!exists)
                    {
                        temp.node.Name = temp.getID().ToString();
                        node.Nodes.Add(temp.node);
                    }
                    
                }
                foreach (Player p in players)
                {
                    if(!inuse.Contains(p))//nolonger in tree
                        node.Nodes.Remove(p.node);
                }
                players = inuse;
                
            }
        }
        public int getID() { return ID; }
        
        //clears the Speakers
        public void clearSpeaker()
        {
            foreach (Player p in players)
                p.setSpeaking(false);
        }
        //set the Speakers
        public void setSpeaker(int ID)
        {
            foreach (Player p in players)
            {
                if (p.getID() == ID)
                    p.setSpeaking(true);

            }

        }
        public override string ToString()
        {
            string a = "(" + ID + " , " + parent + " , " + Name + ")\n";
            foreach (Player p in players)
                a += "\t" + p.getID() + " " + p.NickName + " " + p.getSpeaking() + "\n";
            return a;
        }
    }
    //represents a player on the TS server
    class Player
    {
        private int ID;//player ID
        public int getID() { return ID; }
        public void setID(int id) { ID = id; ;}

        public string NickName;//players nickname
        public String getNickName() { return NickName; }
        private bool speaking = false;//if true the player is speaking
        public bool getSpeaking() { return speaking; }//returns speaking
        public void setSpeaking(bool a)//if true will set speaking to true and will change icon to appropreat color
        { 
            speaking = a;
            if (speaking)
            {
                node.ImageIndex = 2;
                node.SelectedImageIndex = 2;
            }
            else
            {
                node.ImageIndex = 1;
                node.SelectedImageIndex = 1;
            }

        }
        public TreeNode node;//the node which represents the obj
        public Player(int id, String nick)
        {
            node = new TreeNode(nick,1,1);
            this.ID = id;
            this.NickName = nick;
        }

    }
}//end namespace
